#include "precompiled.h"
#include "enhancement.h"
using namespace std;
using namespace weo::engine::item;
using namespace weo::engine::character::shaman;

Enhancement::Enhancement()
{
    Meleedps::Init();
    m_Classnr = weo::engine::character::Character::isAllowed("Shaman");
    m_Basevalues.push_back(Attribute("Agility",59));
    m_Basevalues.push_back(Attribute("Intellect",103));
    m_Basevalues.push_back(Attribute("Strength",103));
    m_Basevalues.push_back(Attribute("Stamina",116));
    m_Basevalues.push_back(Attribute("Spirit",122));
    m_Basevalues.push_back(Attribute("CritSpellRating",2.2f*CritSpellRating));
    m_Basevalues.push_back(Attribute("CritRating",1.66f*CritRating)); // crit
    m_Basevalues.push_back(Attribute("AttackPower",120)); // ap
    m_Basevalues.push_back(Attribute("Health",2979)); // health
    m_Basevalues.push_back(Attribute("Mana",2678)); // mana
    m_Basevalues.push_back(Attribute("HitRating",9*HitRating)); // hr
    m_Basevalues.push_back(Attribute("CritRating",5*CritRating)); // cr
    m_Equip.Mode(0);
    BOOST_FOREACH(Attribute attr, m_Basevalues)
    {
        handleAttribute(attr);
    }
    // base settings finished
}
float Enhancement::evaluateTF()
{
    float runtime = 600;
    float wdps1 = m_Equip[46];
    float wdps2 = m_Equip[48];
    float ws1 = m_Equip[47];
    float ws2 = m_Equip[49];
    float baseap = m_Equip[30];
    float crit = m_Equip[6]/CritRating - 0.6f;
    float hit = m_Equip[7]/HitRating;
    float haste = m_Equip[10]/HasteRating;
    float expertise = m_Equip[51]/ExpertiseRating;
    // -- glancing blow calculation -- //
    float glancingreduction = 25;
    float glancingprob = 25;
    float glancing = glancingreduction * glancingprob / 100;
    // -- some init values -- //
    float dodge = max<float>(6.5f - expertise * 0.25f , 0.0f);
    float hitwhite = min<float>(100 - 9 - 19 + hit, 100);
    float hityellow = min<float>(100 - 9 + hit, 100);
    float avghit = (hityellow + hitwhite) / 200;
    float noncrit = (1 - crit / 100 );
    float minwftime = 3.0f;
    float flurryvalue = 0.30f;
    float asmh = ws1 / ((1 + flurryvalue) * (1 + haste / 100)); // fully flurried attack speed
    float asoh = ws2 / ((1 + flurryvalue) * (1 + haste / 100)); // fully flurried attack speed
    float mhswingsin3 = minwftime/asmh;
    float ohswingsin3 = minwftime/asoh;
    float wfprocprobmh = 0.2f/mhswingsin3;
    float wfprocproboh = 0.2f/ohswingsin3;
    float wfprocprob = wfprocprobmh+wfprocproboh-wfprocprobmh*wfprocproboh;
    // -- some init values -- //
    float attackspeed = (asmh+asoh)/2;
    float aatime = attackspeed /2 ; // how much time for each auto attack  (3*attackspeed/(2*3)
    float flurryprob = 1.0f - pow(noncrit, 3.0f + 2.0f * (1.0f - dodge / 100.0f) * (1.0f - pow(1.0f-wfprocprob,aatime) + aatime * (3.0f/10.0f)));
    //float wsmh = (ws1 / (1 + haste / 100)) * (1 - flurryprob) + (ws1 / ((1 + haste / 100) * (1 + flurryvalue))) * flurryprob; // actual mh ws (flurried/hasted)
    //float wsoh = (ws2 / (1 + haste / 100)) * (1 - flurryprob) + (ws2 / ((1 + haste / 100) * (1 + flurryvalue))) * flurryprob; // actual oh ws (flurried/hasted)
    float wsmh = ws1 / ((1 + haste/100) * (1 + flurryprob*flurryvalue));
    float wsoh = ws2 / ((1 + haste/100) * (1 + flurryprob*flurryvalue));
    float mhhitcount = (hitwhite - dodge) * 1/100.0f * (runtime/wsmh);
    float ohhitcount = (hitwhite - dodge) * 1/100.0f * (runtime/wsoh);
    float avgspeed = (wsmh + wsoh) / 2;
    float ssmhhitcount = (hityellow - dodge) * 1/100.0f * (runtime/10);
    float ssohhitcount = (hityellow - dodge) * 1/100.0f * (hityellow - dodge) * 1/100.0f * (runtime/10);
    float exwftime = max<float>(avgspeed / wfprocprob, minwftime); // expected wf time
    float wfproc = avgspeed / exwftime; // wf proc prob  (should be 36% after all, only different for daggers)
    float wfprocmh = ws2 / (ws1 + ws2) * wfproc; // mh wf proc prob
    float wfprocoh = ws1 / (ws1 + ws2) * wfproc; // oh wf proc prob
    // hit count calculation
    float wfmaxcount = runtime/minwftime;
    float wfprocmhcount = (hityellow - dodge) * 1/100.0f * wfprocmh * mhhitcount;
    float wfprocohcount = (hityellow - dodge) * 1/100.0f * wfprocoh * ohhitcount;
    float ssmaxwfprocs = min<float>(wfmaxcount - wfprocmhcount - wfprocohcount,runtime/10.0f);
    float ssmhwfproccount =  min<float>(ssmaxwfprocs, ssmhhitcount * wfprocmh);
    float ssohwfproccount =  min<float>(ssmaxwfprocs, (ssohhitcount - ssmhwfproccount) * wfprocoh);
    wfprocmhcount += ssmhwfproccount;
    wfprocohcount += ssohwfproccount;
        // -- ap calculation below, first we calc the hit count in 10s for UR, then use this to calculate UR uptime) -- //
    float hitsin10 = 0.01f * (((hitwhite - glancingprob - dodge) * (2 * 10.0f / avgspeed)) + (hityellow - dodge) * (2.0f * (10.0f/exwftime) + 2.0));
    if(m_Equip.hasSpecial(440572))
    {
        baseap += 137.5f;
    }
    if(m_Equip.hasSpecial(636071))
    {
        baseap += 70;
    }
    float shocktime = 6.0f;
    if(m_Equip.hasSpecial(505185))
    {
        baseap += (1.0f - pow(0.5f,2))*(110*(10.0f/(2*shocktime)));
    }
    float ap = baseap * (1 + 0.1f * (1 - pow(noncrit, hitsin10)));
    float wfap = 475;
    if(m_Equip.hasSpecial(1134694))
    {
        wfap += 80;
    }
    if(m_Equip.hasSpecial(445202))
    {
        crit *=1.03f;
    }
    // -- actual dps calculation below -- //
    float shockdps = (0.84f+0.03f)*(1+0.05f*0.5f)*(797 + 675 + (2.0f/3.0f + 1.5f/3.5f) * ap * 0.3f)/(2 * shocktime);
    float damageadjustments = (hitwhite + crit - dodge - glancing)  / (hitwhite - dodge);
    float mhwhitedmg = 1.1f * damageadjustments * (wdps1 + ap / 14) * ws1;
    float ohwhitedmg = 0.5f * 1.1f * damageadjustments * (wdps2 + ap / 14) * ws2;
    float mhwfdmg = 0.01f * 1.1f * (100 + crit) * 2 * 1.4f * (wdps1 + (wfap+ap) / 14) * ws1;
    float ohwfdmg = 0.01f * 1.1f * 0.5f * (100 + crit) * 2 * 1.4f * (wdps2 + (2*wfap+ap) / 14) * ws2;
    float ssmhdmg = 0.01f * 1.1f * (100 + crit ) * (wdps1 + ap / 14) * ws1;
    float ssohdmg = 0.01f * 1.1f * 0.5f * (100 + crit) * (wdps2 + ap / 14) * ws2;
    float mhdps = (mhwhitedmg * mhhitcount + mhwfdmg * wfprocmhcount +  ssmhdmg * ssmhhitcount)/runtime;
    float ohdps = (ohwhitedmg * ohhitcount + ohwfdmg * wfprocohcount +  ssohdmg * ssohhitcount)/runtime;
    float armorreduction = max<float>((MobArmor - m_Equip[50])/(MobArmor - m_Equip[50] + 10557.5f),0);
    float dps = (1.0f-armorreduction) * (mhdps + ohdps);
    if(m_Equip.hasSpecial(110002))
    {
       dps *= 1.04f;
    }
    float addDps = 0;
    if(m_Equip.hasSpecial(182342))
    {
        float syphTime = 6.0f;
        if((m_Equip.getItem(14)->Id() == 32262) && (m_Equip.getItem(15)->Id() == 32262))
        {
            syphTime = 12.0f;
        }
        addDps = (((hitwhite + hityellow) / 200.0f) * (20/wsmh + 20/wsoh)) / (60/syphTime); // one ppm
    }
    return m_CondFactor * (dps + shockdps + addDps);
}
void Enhancement::filterEquip(std::vector<std::vector<const weo::engine::item::Item*>>& items)
{
    for(unsigned int i = 0;i<10;i++)
    {
        int subtype = 3;
        if(i == 1 || i == 3)
        {
            vector<const Item*> temp = m_Database->getPtrbyType(1,i+1);
            items.push_back(temp);
        }
        else
        {
            vector<const Item*> temp = m_Database->getPtrbyType(1,i+1,3); // mail		
            vector<const Item*> temp2 = m_Database->getPtrbyType(1,i+1,2); // leather
            vector<const Item*>::iterator it;
            for(it=temp2.begin();it!=temp2.end();it++)
            {
                temp.push_back(*it);
            }
            items.push_back(temp);
        }
    }
    items.push_back(m_Database->getPtrbyType(2,0));
    items.push_back(m_Database->getPtrbyType(2,0));
    items.push_back(m_Database->getPtrbyType(3,0));
    items.push_back(m_Database->getPtrbyType(3,0));
    // main hand weapons
    vector<const Item*> mainhand;
    for(int i = 2;i<6;i++)
    {
        for(int j = 1;j<3;j++)
        {
            vector<const Item*> weapon = m_Database->getPtrbyType(4,j,i);
            vector<const Item*>::iterator it;
            for(it=weapon.begin();it!=weapon.end();it++)
            {
                mainhand.push_back(*it);
            }
        }
    }
    // off hand weapons
    vector<const Item*> offhand;
    for(int i = 2;i<6;i++)
    {
        for(int j = 1;j<4;j+=2)
        {
            vector<const Item*> weapon = m_Database->getPtrbyType(4,j,i);
            vector<const Item*>::iterator it;
            for(it=weapon.begin();it!=weapon.end();it++)
            {
                offhand.push_back(*it);
            }
        }
    }
    items.push_back(mainhand);
    items.push_back(offhand);
    items.push_back(m_Database->getPtrbyType(4,5,15)); // totems
    items.push_back(m_Database->getPtrbyType(5,0));
    Meleedps::filterEquip(items);
}
void Enhancement::handleAttribute(const Attribute& attr)
{
    switch(attr.Type())
    {
    default:
        Meleedps::handleAttribute(attr);
        break;
    }
}
